package com.filldream.fastboot.common.util;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.Map.Entry;
import com.filldream.fastboot.common.util.entity.HttpDTO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.http.HttpStatus;
import org.apache.http.NameValuePair;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;

/**
 * @author RickSun
 * @Description Http请求工具类
 * @version 2.0
 * @see UrlUtil
 **/
@Slf4j
public class HttpUtil {

	/**
	 * setConnectTimeout：设置连接超时时间，单位毫秒。
	 * setConnectionRequestTimeout：设置从connect Manager(连接池)获取Connection
	 * 超时时间，单位毫秒。这个属性是新加的属性，因为目前版本是可以共享连接池的。
	 * setSocketTimeout：请求获取数据的超时时间(即响应时间)，单位毫秒。 如果访问一个接口，多少时间内无法返回数据，就直接放弃此次调用。
	 */

	//连接超时时间，单位毫秒。
	private static final int CONNECT_TIMEOUT = 3000;
	
	// 请求获取数据的超时时间(即响应时间)，单位毫秒。
	private static final int SOCKET_TIMEOUT = 3000;

	static class RequestContent{
		//请求参数
		private Map<String,String> params;
		//自定义请求body
		private String body;

		RequestContent(Map<String,String> params){
			this.params = params;
			this.body = null;
		}

		RequestContent(String body){
			this.params = null;
			this.body = body;
		}

	}

	/**
	 * @author RickSun
	 * @Description Url请求
	 * @param url 请求地址
	 * 不带任何参数
	 **/
	public static HttpDTO doGet(String url) throws Exception {
		return doGet(url, null, null);
	}

	/**
	 * @author RickSun
	 * @Description Url请求
	 * @param params 请求参数
	 **/
	public static HttpDTO doGet(String url, Map<String, String> params) throws Exception {
		return doGet(url, null, params);
	}

	/**
	 * @author RickSun
	 * @Description Get请求
	 * @param url 请求地址
	 * @param headers 请求头
	 * @param params 请求参数
	 **/
	public static HttpDTO doGet(String url, Map<String, String> headers, Map<String, String> params) throws Exception {
		log.info("请求地址:{},请求参数:{},请求头:{}" ,url,params,headers);
		//创建httpClient对象
		CloseableHttpClient httpClient = HttpClients.createDefault();

		//创建访问的地址
		URIBuilder uriBuilder = new URIBuilder(url);
		if (params != null) {
			Set<Entry<String, String>> entrySet = params.entrySet();
			for (Entry<String, String> entry : entrySet) {
				uriBuilder.setParameter(entry.getKey(), entry.getValue());
			}
		}

		//创建http对象
		HttpGet httpGet = new HttpGet(uriBuilder.build());

		RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(CONNECT_TIMEOUT).setSocketTimeout(SOCKET_TIMEOUT).build();
		httpGet.setConfig(requestConfig);
		
		//设置请求头
		packageHeader(headers, httpGet);

		//创建httpResponse对象
		CloseableHttpResponse httpResponse = null;
		try {
			// 执行请求并获得响应结果
			return getResult(httpResponse, httpClient, httpGet);
		} finally {
			// 释放资源
			close(httpResponse, httpClient);
		}
	}

	/**
	 * @author RickSun
	 * @Description Post请求
	 * @param url	请求地址
	 * 不带任何参数
	 **/
	public static HttpDTO doPost(String url) throws Exception {
		return doPost(url, null, null);
	}

	/**
	 * @author RickSun
	 * @Description Post请求
	 * @param url	请求地址
	 * @param params 请求参数
	 **/
	public static HttpDTO doPost(String url, Map<String, String> params) throws Exception {
		return doPost(url, null, new RequestContent(params));
	}

	/**
	 * @author RickSun
	 * @Description Post请求
	 * @param url	请求地址
	 * @param body	自定义请求body
	 **/
	public static HttpDTO doPost(String url, String body) throws Exception {
		return doPost(url, null, new RequestContent(body));
	}

	/**
	 * @author RickSun
	 * @Description Post请求
	 * @param url	请求地址
	 * @param headers 请求头
	 * @param requestContent 请求参数
	 **/
	public static HttpDTO doPost(String url, Map<String, String> headers, RequestContent requestContent) throws Exception {
		log.info("请求地址:{},请求参数:{},请求头:{}" ,url,requestContent,headers);
		// 创建httpClient对象
		CloseableHttpClient httpClient = HttpClients.createDefault();

		// 创建http对象
		HttpPost httpPost = new HttpPost(url);

		RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(CONNECT_TIMEOUT).setSocketTimeout(SOCKET_TIMEOUT).build();
		httpPost.setConfig(requestConfig);
		// 设置请求头
		/*httpPost.setHeader("Cookie", "");
		httpPost.setHeader("Connection", "keep-alive");
		httpPost.setHeader("Accept", "application/json");
		httpPost.setHeader("Accept-Language", "zh-CN,zh;q=0.9");
		httpPost.setHeader("Accept-Encoding", "gzip, deflate, br");
		httpPost.setHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36");*/
		packageHeader(headers, httpPost);
		
		// 封装请求参数
		packageParam(requestContent, httpPost);

		// 创建httpResponse对象
		CloseableHttpResponse httpResponse = null;

		try {
			// 执行请求并获得响应结果
			return getResult(httpResponse, httpClient, httpPost);
		} finally {
			// 释放资源
			close(httpResponse, httpClient);
		}
	}

	/**
	 * @author RickSun
	 * @Description Put请求
	 * 不带参数
	 **/
	public static HttpDTO doPut(String url) throws Exception {
		return doPut(url);
	}

	/**
	 * @author RickSun
	 * @Description Put请求
	 * @param params 请求参数
	 **/
	public static HttpDTO doPut(String url, Map<String, String> params) throws Exception {
		CloseableHttpClient httpClient = HttpClients.createDefault();
		HttpPut httpPut = new HttpPut(url);
		RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(CONNECT_TIMEOUT).setSocketTimeout(SOCKET_TIMEOUT).build();
		httpPut.setConfig(requestConfig);
		
		packageParam(new RequestContent(params), httpPut);

		CloseableHttpResponse httpResponse = null;

		try {
			return getResult(httpResponse, httpClient, httpPut);
		} finally {
			close(httpResponse, httpClient);
		}
	}

	/**
	 * @author RickSun
	 * @Description Delete请求
	 * @param url	请求地址
	 **/
	public static HttpDTO doDelete(String url) throws Exception {
		CloseableHttpClient httpClient = HttpClients.createDefault();
		HttpDelete httpDelete = new HttpDelete(url);
		RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(CONNECT_TIMEOUT).setSocketTimeout(SOCKET_TIMEOUT).build();
		httpDelete.setConfig(requestConfig);

		CloseableHttpResponse httpResponse = null;
		try {
			return getResult(httpResponse, httpClient, httpDelete);
		} finally {
			close(httpResponse, httpClient);
		}
	}

	/**
	 * @author RickSun
	 * @Description Delete请求
	 * @param url	请求地址
	 * @param params	请求参数
	 **/
	public static HttpDTO doDelete(String url, Map<String, String> params) throws Exception {
		if (params == null) {
			params = new HashMap<String, String>();
		}

		params.put("_method", "delete");
		return doPost(url, params);
	}

	/**
	 * @author RickSun
	 * @Description 添加请求头
	 * @param params	请求头参数
	 **/
	public static void packageHeader(Map<String, String> params, HttpRequestBase httpMethod) {
		// 封装请求头
		if (params != null) {
			Set<Entry<String, String>> entrySet = params.entrySet();
			for (Entry<String, String> entry : entrySet) {
				// 设置到请求头到HttpRequestBase对象中
				httpMethod.setHeader(entry.getKey(), entry.getValue());
			}
		}
	}

	/**
	 * @author RickSun
	 * @Description 封装请求参数
	 **/
	public static void packageParam(RequestContent requestContent, HttpEntityEnclosingRequestBase httpMethod) {
		log.info("请求参数:" + JsonUtil.toJson(requestContent));
		// 封装请求参数
		if (requestContent != null) {
			if(!ObjectUtils.isEmpty( requestContent.params )){
				List<NameValuePair> nvps = new ArrayList<NameValuePair>();
				Set<Entry<String, String>> entrySet = requestContent.params.entrySet();
				for (Entry<String, String> entry : entrySet) {
					nvps.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
				}

				// 设置到请求的http对象中
				httpMethod.setEntity(new UrlEncodedFormEntity(nvps, StandardCharsets.UTF_8));
			}else if( !ObjectUtils.isEmpty( requestContent.body ) )  {
				httpMethod.setEntity(new StringEntity(requestContent.body,StandardCharsets.UTF_8));
			}
		}
	}

	/**
	 * @author RickSun
	 * @Description 获取响应结果
	 **/
	public static HttpDTO getResult(CloseableHttpResponse httpResponse, CloseableHttpClient httpClient, HttpRequestBase httpMethod) throws Exception {
		// 执行请求
		httpResponse = httpClient.execute(httpMethod);
		// 获取返回结果
		if (httpResponse != null && httpResponse.getStatusLine() != null) {
			String content = "";
			if (httpResponse.getEntity() != null) {
				content = EntityUtils.toString(httpResponse.getEntity(), StandardCharsets.UTF_8);
			}
			return new HttpDTO(httpResponse.getStatusLine().getStatusCode(), content);
		}
		return new HttpDTO(HttpStatus.SC_INTERNAL_SERVER_ERROR);
	}

	/**
	 * @author RickSun
	 * @Description 释放资源
	 **/
	public static void close(CloseableHttpResponse httpResponse, CloseableHttpClient httpClient) throws IOException {
		// 释放资源
		if (httpResponse != null) {
			httpResponse.close();
		}
		if (httpClient != null) {
			httpClient.close();
		}
	}

}